"""
大语言模型客户端
用于意图识别和自然语言处理
使用百度千帆API
"""

import os
from typing import Dict, List, Optional
from openai import OpenAI
import json

class LLMClient:
    """大语言模型客户端类"""
    
    def __init__(self, api_key: str = None, base_url: str = None):
        self.api_key = "sk-dfa98e0989bb4ef7a41b63e38ed8a661"
        self.base_url = "https://dashscope.aliyuncs.com/compatible-mode/v1" #  base_url or 
        
        if self.api_key:
            self.client = OpenAI(
                api_key=self.api_key,
                base_url=self.base_url
            )
        else:
            print("警告: 未设置OPENAI_API_KEY环境变量")
            self.client = None
    
    def recognize_intent(self, user_input: str, use_fast_path: bool = True) -> str:
        """
        识别用户输入的意图
        
        Args:
            user_input: 用户输入的自然语言文本
            use_fast_path: 是否优先使用快速路径（关键词匹配，无需API调用）
            
        Returns:
            识别出的意图类型
        """
        # 快速路径：优先使用本地关键词匹配（无需API调用，响应快）
        if use_fast_path:
            fast_intent = self._fast_intent_recognition(user_input)
            if fast_intent:
                return self._normalize_intent(fast_intent)
        
        # 如果快速路径无法识别，再尝试API调用
        if not self.api_key or not self.client:
            # 尝试从环境变量热加载（支持运行中在 UI 里设置）
            env_key = os.getenv('OPENAI_API_KEY')
            if env_key:
                self.api_key = env_key
                try:
                    self.client = OpenAI(
                        api_key=self.api_key,
                        base_url=self.base_url
                    )
                except Exception:
                    pass
            else:
                fallback_intent = self._fallback_intent_recognition(user_input)
                return self._normalize_intent(fallback_intent)
        
        try:
            prompt = self._create_intent_recognition_prompt(user_input)
            
            messages = [
                {"role": "system", "content": "你是一个专业的意图识别助手，专门识别电子产品网购平台客服对话中的用户意图。"},
                {"role": "user", "content": prompt}
            ]
            
            response = self.client.chat.completions.create(
                model="qwen-plus",
                messages=messages
            )
            
            intent = response.choices[0].message.content.strip().lower()
            return self._normalize_intent(intent)
            
        except Exception as e:
            print(f"LLM API调用失败: {e}")
            # API失败时回退到本地匹配
            fallback_intent = self._fallback_intent_recognition(user_input)
            return self._normalize_intent(fallback_intent)
    
    def _fast_intent_recognition(self, user_input: str) -> str:
        """
        快速意图识别（基于关键词匹配，无需API调用）
        
        Args:
            user_input: 用户输入
            
        Returns:
            识别出的意图类型（标准化后的中文），如果无法识别则返回None
        """
        user_input_lower = user_input.lower().strip()
        
        # 精确匹配关键词（优先级高）
        exact_keywords = {
            '订单查询': ['查询订单', '查看订单', '我的订单', '订单状态', '订单列表', '订单查询'],
            '订单取消': ['取消订单', '撤销订单', '退订订单'],
            '产品信息': ['产品信息', '商品信息', '产品介绍', '商品介绍'],
            '退款申请': ['申请退款', '我要退款', '退款申请'],
            '投诉': ['投诉', '投诉建议', '我要投诉'],
            '问候': ['你好', '您好', 'hello', 'hi', '早上好', '下午好', '晚上好'],
            '告别': ['再见', '拜拜', 'bye', 'goodbye', '退出', '结束']
        }
        
        for intent, keywords in exact_keywords.items():
            for keyword in keywords:
                if keyword in user_input_lower:
                    return intent
        
        # 子意图识别（只识别CS产品，必须包含CS关键词）
        # 严格匹配：只有当输入明确包含CS产品关键词时才识别为对应的子意图
        # 例如："CS手机"、"cs手机一号"会被识别为phone，但"huawei手机"不会
        cs_product_keywords = {
            'computer': ['cs电脑', 'cs电脑一号', 'cs电脑二号', 'cs电脑三号'],
            'phone': ['cs手机', 'cs手机一号', 'cs手机二号', 'cs手机三号'],
            'pad': ['cs平板', 'cs平板一号', 'cs平板二号', 'cs平板三号'],
            'earset': ['cs耳机', 'cs耳机一号', 'cs耳机二号', 'cs耳机三号']
        }
        
        # 首先检查是否包含CS产品关键词（优先级最高）
        for intent, keywords in cs_product_keywords.items():
            for keyword in keywords:
                if keyword in user_input_lower:
                    return intent  # 返回英文子意图，后续会保持英文
        
        # 如果输入包含通用产品词（如"手机"、"电脑"）但不包含CS，检查是否有其他品牌
        # 如果有其他品牌（如huawei、apple、samsung等），返回unknown
        generic_product_words = ['手机', '电脑', '笔记本', '平板', '耳机', 'phone', 'computer', 'laptop', 'pad', 'earset']
        brand_keywords = ['huawei', '华为', 'apple', '苹果', 'samsung', '三星', 'xiaomi', '小米', 'oppo', 'vivo', 'honor', '荣耀', 
                         'thinkpad', 'lenovo', '联想', 'dell', 'hp', '惠普', 'asus', '华硕', 'sony', '索尼']
        
        has_generic_product = any(word in user_input_lower for word in generic_product_words)
        has_brand = any(brand in user_input_lower for brand in brand_keywords)
        has_cs = 'cs' in user_input_lower
        
        # 如果包含通用产品词，但没有CS关键词，且有其他品牌，返回unknown
        if has_generic_product and not has_cs and has_brand:
            return 'unknown'
        
        # 如果只包含通用产品词但没有CS也没有其他品牌，可能是询问产品信息（但不确定是CS产品）
        # 这种情况下，如果用户明确说"我要买手机"等，可以返回unknown，因为系统只支持CS产品
        if has_generic_product and not has_cs and not has_brand:
            # 检查是否是明确的购买/查询意图
            purchase_keywords = ['买', '购买', '想买', '要买', '咨询', '了解', '查询', '查看', '介绍']
            if any(keyword in user_input_lower for keyword in purchase_keywords):
                return 'unknown'  # 明确询问非CS产品，返回unknown
        
        # 模糊匹配关键词（优先级较低，但排除已经被子意图匹配的关键词）
        # 注意：避免"电脑"、"手机"等关键词被匹配到"产品信息"
        keyword_mapping = {
            '订单查询': ['订单', '查询', '查看'],
            '订单取消': ['取消', '退订', '撤销'],
            '产品信息': ['产品', '商品', '规格', '参数', '价格'],  # 注意：不包含"电脑"、"手机"等，这些已经在子意图中处理
            '退款申请': ['退款', '退货', '退钱'],
            '投诉': ['建议', '意见', '问题', '故障'],
            '问候': ['你好', '您好'],
            '告别': ['再见', '拜拜']
        }
        
        # 排除已经被子意图匹配的关键词，避免重复匹配
        excluded_keywords = ['电脑', '笔记本', '手机', '智能手机', '平板', '平板电脑', '耳机', 'cs电脑', 'cs手机', 'cs平板', 'cs耳机']
        
        for intent, keywords in keyword_mapping.items():
            for keyword in keywords:
                if keyword in user_input_lower and keyword not in excluded_keywords:
                    return intent
        
        return None  # 无法通过快速路径识别
    
    def _create_intent_recognition_prompt(self, user_input: str) -> str:
        """创建意图识别的提示词"""
        prompt = f"""
        请分析以下用户输入，识别其意图类型。从以下选项中选择最匹配的一个：
        
        主要意图：
        1. order_inquiry - 订单查询相关
        2. order_cancel - 订单取消相关
        3. product_info - 产品信息咨询（通用）
        4. refund_request - 退款申请
        5. complaint - 投诉或建议
        6. greeting - 问候语
        7. goodbye - 告别语
        8. unknown - 无法识别的意图
        
        产品子意图（只识别CS产品，必须包含CS关键词）：
        9. computer - CS笔记本电脑相关（如CS电脑、CS电脑一号等，必须包含CS）
        10. phone - CS智能手机相关（如CS手机、CS手机一号等，必须包含CS）
        11. pad - CS平板电脑相关（如CS平板、CS平板一号等，必须包含CS）
        12. earset - CS耳机相关（如CS耳机、CS耳机一号等，必须包含CS）
        
        重要规则：
        - 只有当用户输入明确包含CS关键词时，才识别为对应的产品子意图（computer/phone/pad/earset）
        - 如果用户询问非CS产品（如huawei手机、apple手机等），必须返回unknown
        - 如果用户只输入"手机"、"电脑"等通用词但没有CS关键词，且是明确的购买/查询意图，返回unknown
        
        用户输入: "{user_input}"
        
        请只回答意图类型的英文名称，不要其他内容。如果是询问CS产品，优先选择对应的子意图（computer/phone/pad/earset）。如果是非CS产品，返回unknown。
        """
        return prompt
    
    def _normalize_intent(self, intent: str) -> str:
        """标准化意图名称"""
        intent_mapping = {
            # 英文 -> 中文或保持英文（子意图保持英文以便匹配DSL规则）
            'order_inquiry': 'order_inquiry',
            'order_cancel': 'order_cancel',
            'product_info': 'product_info',
            'refund_request': 'refund_request',
            'complaint': 'complaint',
            'greeting': 'greeting',
            'goodbye': 'goodbye',
            'unknown': 'unknown',
            # 子意图保持英文（用于匹配DSL中的WHEN COMPUTER等规则）
            'computer': 'computer',
            'phone': 'phone',
            'pad': 'pad',
            'earset': 'earset',
            # 中文同义词 -> 中文标准或英文
            '订单查询': 'order_inquiry',
            '查询订单': 'order_inquiry',
            '查看订单': 'order_inquiry',
            '订单取消': 'order_cancel',
            '取消订单': 'order_cancel',
            '产品信息': 'product_info',
            '退款': 'refund_request',
            '退款申请': 'refund_request',
            '投诉': 'complaint',
            '问候': 'greeting',
            '你好': 'greeting',
            '您好': 'greeting',
            '告别': 'goodbye',
            '再见': 'goodbye'
        }
        
        intent_lower = intent.lower().strip()
        # 如果意图是unknown或未知，直接返回unknown（保持英文以便后续处理）
        if intent_lower in ['unknown', '未知']:
            return 'unknown'
        return intent_mapping.get(intent_lower, 'unknown')
    
    def _fallback_intent_recognition(self, user_input: str) -> str:
        """
        当LLM API不可用时的备用意图识别方法
        基于关键词匹配，只识别CS产品
        """
        user_input_lower = user_input.lower()
        
        # 首先检查CS产品关键词（只识别CS产品）
        cs_product_keywords = {
            'computer': ['cs电脑', 'cs电脑一号', 'cs电脑二号', 'cs电脑三号'],
            'phone': ['cs手机', 'cs手机一号', 'cs手机二号', 'cs手机三号'],
            'pad': ['cs平板', 'cs平板一号', 'cs平板二号', 'cs平板三号'],
            'earset': ['cs耳机', 'cs耳机一号', 'cs耳机二号', 'cs耳机三号']
        }
        
        for intent, keywords in cs_product_keywords.items():
            for keyword in keywords:
                if keyword in user_input_lower:
                    return intent
        
        # 检查是否有其他品牌（非CS产品）
        brand_keywords = ['huawei', '华为', 'apple', '苹果', 'samsung', '三星', 'xiaomi', '小米', 'oppo', 'vivo', 'honor', '荣耀', 
                         'thinkpad', 'lenovo', '联想', 'dell', 'hp', '惠普', 'asus', '华硕', 'sony', '索尼']
        generic_product_words = ['手机', '电脑', '笔记本', '平板', '耳机', 'phone', 'computer', 'laptop', 'pad', 'earset']
        
        has_generic_product = any(word in user_input_lower for word in generic_product_words)
        has_brand = any(brand in user_input_lower for brand in brand_keywords)
        has_cs = 'cs' in user_input_lower
        
        # 如果包含通用产品词，但没有CS关键词，且有其他品牌，返回unknown
        if has_generic_product and not has_cs and has_brand:
            return 'unknown'
        
        # 如果只包含通用产品词但没有CS也没有其他品牌，且是明确的购买/查询意图，返回unknown
        if has_generic_product and not has_cs and not has_brand:
            purchase_keywords = ['买', '购买', '想买', '要买', '咨询', '了解', '查询', '查看', '介绍']
            if any(keyword in user_input_lower for keyword in purchase_keywords):
                return 'unknown'
        
        # 关键词映射（其他意图）
        keyword_mapping = {
            'order_inquiry': ['订单', '查询', '查看', '我的订单', '订单状态', 'order', 'query'],
            'order_cancel': ['取消', '退订', '撤销', 'cancel', '取消订单'],
            'product_info': [
                '产品', '商品', '规格', '参数', '价格', '品牌', 'product', 'item'
            ],
            'refund_request': ['退款', '退货', '退钱', 'refund', 'return'],
            'complaint': ['投诉', '建议', '意见', '问题', '故障', 'complaint', 'issue'],
            'greeting': ['你好', '您好', 'hello', 'hi', '早上好', '下午好', '晚上好'],
            'goodbye': ['再见', '拜拜', 'bye', 'goodbye', '退出', '结束']
        }
        
        for intent, keywords in keyword_mapping.items():
            for keyword in keywords:
                if keyword in user_input_lower:
                    return intent
        
        return 'unknown'
    
    def generate_response(self, context: str, user_input: str) -> str:
        """
        基于上下文生成回复
        
        Args:
            context: 对话上下文
            user_input: 用户输入
            
        Returns:
            生成的回复
        """
        if not self.api_key or not self.client:
            env_key = os.getenv('OPENAI_API_KEY')
            if env_key:
                self.api_key = env_key
                print(self.api_key)
                try:
                    self.client = OpenAI(
                        api_key=self.api_key,
                        base_url=self.base_url
                    )
                except Exception:
                    pass
            else:
                return self._fallback_response_generation(user_input)
        
        try:
            messages = [
                {"role": "system", "content": "你是一个专业的电子产品网购平台客服助手，请根据用户的问题提供友好、专业的回复。"},
                {"role": "user", "content": f"上下文: {context}\n用户问题: {user_input}"}
            ]
            
            response = self.client.chat.completions.create(
                model="qwen-plus",
                messages=messages
            )
            
            return response.choices[0].message.content.strip()
            
        except Exception as e:
            print(f"LLM API调用失败: {e}")
            return self._fallback_response_generation(user_input)
    
    def generate_response_with_memory(self, context: str, user_input: str, longmemory: List[Dict] = None) -> str:
        """
        基于上下文和长期记忆生成回复
        
        Args:
            context: 对话上下文
            user_input: 用户输入
            longmemory: 长期记忆数组，包含历史对话记录
            
        Returns:
            生成的回复
        """
        if not self.api_key or not self.client:
            env_key = os.getenv('OPENAI_API_KEY')
            if env_key:
                self.api_key = env_key
                print(self.api_key)
                try:
                    self.client = OpenAI(
                        api_key=self.api_key,
                        base_url=self.base_url
                    )
                except Exception:
                    pass
            else:
                return self._fallback_response_generation(user_input)
        
        try:
            # 构建消息列表
            messages = [
                {"role": "system", "content": "你是一个专业的电子产品网购平台客服助手，请根据用户的问题和对话历史提供友好、专业的回复。能够理解上下文，记住之前讨论的内容。"}
            ]
            
            # 将长期记忆中的对话添加到消息列表（最多最近10条，即5轮对话，减少API负载）
            if longmemory:
                # 获取最近的记忆（最多10条消息，减少token消耗和响应时间）
                recent_memory = longmemory[-10:] if len(longmemory) > 10 else longmemory
                
                for memory_entry in recent_memory:
                    role = memory_entry.get('role', 'user')
                    content = memory_entry.get('content', '')
                    # 只添加有效的角色和内容，并限制内容长度
                    if role in ['user', 'assistant'] and content:
                        # 限制单条消息长度，避免过长
                        if len(content) > 500:
                            content = content[:500] + "..."
                        messages.append({
                            "role": role,
                            "content": content
                        })
            
            # 添加当前用户输入
            messages.append({
                "role": "user",
                "content": f"上下文信息: {context}\n\n当前问题: {user_input}"
            })
            
            response = self.client.chat.completions.create(
                model="qwen-plus",
                messages=messages
            )
            
            return response.choices[0].message.content.strip()
            
        except Exception as e:
            print(f"LLM API调用失败: {e}")
            return self._fallback_response_generation(user_input)
    
    def _fallback_response_generation(self, user_input: str) -> str:
        """备用回复生成方法"""
        responses = {
            'order_inquiry': "正在为您查询订单信息，请稍等...",
            'order_cancel': "我来帮您处理订单取消事宜。",
            'product_info': "很高兴为您介绍我们的产品信息。",
            'refund_request': "我来协助您处理退款申请。",
            'complaint': "非常抱歉给您带来不便，我会认真处理您的问题。",
            'greeting': "您好！欢迎来到我们的电子产品购物平台，有什么可以帮助您的吗？",
            'goodbye': "感谢您的咨询，祝您购物愉快，再见！",
            'unknown': "抱歉，我没有完全理解您的问题，请您详细描述一下，我会尽力帮助您。"
        }
        
        intent = self._fallback_intent_recognition(user_input)
        return responses.get(intent, responses['unknown'])

# 测试函数
if __name__ == "__main__":
    # 测试意图识别
    client = LLMClient()
    
    test_inputs = [
        "你好，我想查询我的订单",
        "我要取消订单12345",
        "这个手机有什么配置？",
        "我要申请退款",
        "你们的服务太差了！",
        "再见"
    ]
    
    for user_input in test_inputs:
        intent = client.recognize_intent(user_input)
        print(f"输入: {user_input}")
        print(f"识别意图: {intent}")
        print("-" * 50)
